#include "sysx/System.h"
#include "mustache.hpp"
#include <string>
#include <locale>
#include <codecvt> 
#include <iomanip>
#include <windows.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <iostream>
#include <set>
#include <locale>
#include <codecvt>

#undef min
#undef max
#include <msdfgen/msdfgen.h>
#include <msdfgen/msdfgen-ext.h>
#include <gli/gli.hpp>

#include "fxcc/core/Dir.h"
#include "fxcc/core/AssertManager.h"
#include "fxcc/core/graph/common/ImageMipmap.h"

#include "fxcc/core/graph/common/gliUtils.h"

using namespace msdfgen;
msdfgen::Shape* shapePtr;

struct SDFChar
{


};

struct SDFFontFile
{
    std::string m_Path;
    std::map<int, SDFChar> m_SDFChars;

    SDFFontFile(const std::string& path) :m_Path(path) {

    };
};

struct FontFileToSDF
{
    std::set<wchar_t> wcharSet;

    struct Desc
    {
        std::string fontPath;
        glm::ivec2 msdfSize{ 32,32 };
        glm::vec2 msdfTranslate{ 0.125, 0.125 };
        float range{ 0.125f };
        std::string msdfDir{"sdfPath"};
        std::string ddsPath{ "msdf.dds" };
        float PxRange()
        {
            return msdfSize.x * range;
        }
    } m_Desc;
    
    std::string fontFamily;

    static std::string Getmsdf_BMP(const wchar_t& ch)
    {
        int charCode = (static_cast<int>(ch));
        std::string charStr = std::to_string(charCode);
        return charStr + ".bmp";
    }

    FontFileToSDF(const Desc& desc) :m_Desc(desc)
    {
        getALlChar();
    }
    int getALlChar()
    {
        FT_Library ft;
        if (FT_Init_FreeType(&ft)) {
            ztclog::error("Could not init FreeType library");
            return false;
        }

        // Load Face From TTF/TTC file
        FT_Face face;
        if (FT_New_Face(ft, m_Desc.fontPath.c_str(), 0, &face)) {
            ztclog::error("Failed to load font");
            return false;
        }

        fontFamily = face->family_name;

        FT_UInt glyphIndex = 0;

        FT_ULong charcode = FT_Get_First_Char(face, &glyphIndex);

        while (glyphIndex != 0) {

            glm::ivec2 m_Size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
            glm::ivec2 m_Bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top);
            glm::ivec2 m_Advance = glm::ivec2(face->glyph->advance.x, face->glyph->advance.y);
            wcharSet.insert(static_cast<wchar_t>(charcode));
            
            charcode = FT_Get_Next_Char(face, charcode, &glyphIndex);

        }

        FT_Done_Face(face);
        FT_Done_FreeType(ft);

        return 0;
    }

    bool LoadChar(const wchar_t& ch)
    {
        return true;
    }
    bool LoadAllChars()
    {

        return true;
    }

    int genDDSFile()
    {
        int msdfWidth = m_Desc.msdfSize.x;
        int msdfHeight = m_Desc.msdfSize.y;
        float msdfTranslateX = m_Desc.msdfTranslate.x;
        float msdfTranslateY = m_Desc.msdfTranslate.y;
        const std::string sdfDir = m_Desc.msdfDir;

        gli::texture2d_array texture(gli::FORMAT_RGB8_UNORM_PACK8, { msdfWidth,msdfHeight }, this->wcharSet.size(), 1);
        int level = texture.levels();
        int layers = texture.layers();
    
        int index = 0;


        fs::path _debugPath = fs::path("_debug_sdf") / fs::path(fontFamily);
        if (!fs::exists(_debugPath))
        {
            fs::create_directories(_debugPath);
        }

        for (const wchar_t& ch : wcharSet)
        {
            int charCode = (static_cast<int>(ch));

            std::string charStr = std::to_string(charCode);
            fs::path sdfPath = fs::path(sdfDir) / Getmsdf_BMP(ch);

            if (!fs::exists(sdfPath))
            {
                continue;
            }
            fxcc::graph::common::ImageMipmap::Desc desc;
            desc.m_wicHdr = 0;
            desc.m_wicPath = sdfPath.string();
            desc.m_wicReq = 3;

            fxcc::graph::common::ImageMipmap image(desc);
       

            fs::path _debufSdfPath = _debugPath / fs::path(Getmsdf_BMP(ch));

            image.m_WICTexture.SaveBMP(_debufSdfPath.string());

            std::size_t imageDataSize = image.m_WICTexture.DataSize();
            std::size_t expectedSizePerSlice = texture.extent(0).x * texture.extent(0).y * desc.m_wicReq * (desc.m_wicHdr ? 4 : 1);

            assert(imageDataSize == expectedSizePerSlice);
            memcpy(texture.data(index, 0, 0), image.m_WICTexture.GetData(), image.m_WICTexture.DataSize());
            index++;

        }

        const std::string ddsPath = m_Desc.ddsPath;

        fxcc::Base::CreateParentDirs(ddsPath);
        gli::save_dds(texture, ddsPath);

        return true;
    }

    int subSDF()
    {
        for (const wchar_t& ch : wcharSet)
        {
            fs::path sdfPath(GetCharPath(ch));
            ztclog::info("sdf Path %s", sdfPath.string().c_str());
            

        }
        return true;
    }
    std::string GetSDFDir()
    {
        fs::path _p(m_Desc.msdfDir);
        fs::path _res = _p / fs::path(fontFamily) ;
        return _res.string();
    };
    std::string GetCharPath(const wchar_t& ch)
    {
        int charCode = (static_cast<int>(ch));
        fs::path _p(m_Desc.msdfDir);
        std::string fileName = Getmsdf_BMP(ch);
        fs::path _res = fs::path(GetSDFDir()) / fileName;
        return _res.string();

    };
    int createSDF()
    {
        const std::string fontPath = m_Desc.fontPath;
        int msdfWidth = m_Desc.msdfSize.x;
        int msdfHeight = m_Desc.msdfSize.y;
        float msdfTranslateX = m_Desc.msdfTranslate.x;
        float msdfTranslateY = m_Desc.msdfTranslate.y;
        const std::string sdfDir = m_Desc.msdfDir;
        
        fs::path _p(m_Desc.msdfDir);
        fs::path _dir = _p / fs::path(fontFamily);

        if (!fs::exists(_dir))
        {
            fs::create_directories(_dir);
        }
        
        auto& shape = *shapePtr;

        if (wcharSet.empty())
        {
            return 0;
        }

        if (FreetypeHandle* ft = initializeFreetype()) {
            if (FontHandle* font = loadFont(ft, fontPath.c_str())) {

                if (!fs::exists(sdfDir))
                    fs::create_directories(sdfDir);

                for (const wchar_t& ch : wcharSet)
                {
                    int charCode = (static_cast<int>(ch));
                    std::string charStr = std::to_string(charCode);
                    fs::path sdfPath(GetCharPath(ch));

                    if (loadGlyph(shape, font, ch, FONT_SCALING_EM_NORMALIZED)) {
                        shape.normalize();
                        //                      max. angle
                        edgeColoringSimple(shape, 3.0);
                        //          output width, height
                        Bitmap<float, 3> msdf({ msdfWidth,msdfHeight });

                        SDFTransformation t(Projection({ (double)msdfWidth,(double)msdfHeight }, { msdfTranslateX, msdfTranslateY }), Range(m_Desc.range));

                        generateMSDF(msdf, shape, t);

                        if (!fs::exists(sdfPath))
                        {
                            ztclog::info("save path %s", sdfPath.string().c_str());
                            saveBmp(msdf, sdfPath.string().c_str());
                        }
                    }
                }
                destroyFont(font);
            }
            deinitializeFreetype(ft);
        }
        
        return 0;

    }
};

int main()
{
    shapePtr = new Shape();;

    ztclog::pushFunction([](const ztclog::Line& l) {std::cout << l.m_Text << std::endl; });

    fs::path _fontDir("C:/Windows/Fonts");

    std::string fileName;
    fileName= "arial.ttf";
    //fileName = "simsun.ttc";

    std::string fileNameNS = fxcc::Base::GetNoSuffixName(fileName);
    fs::path fontPath = _fontDir / fileName;

    //for (const auto it : fs::directory_iterator(_fontDir))
    {
        //fs::path fontPath = it.path();
        //if (fs::is_directory(it))
        //{
        //    continue;
        //}
        std::string fileName = fontPath.filename().string();
        std::string fileNameSelf = fxcc::Base::GetNoSuffixName(fileName);
        std::string suffix = fxcc::Base::GetSuffix(fileName);
        std::string ddsPath = fxcc::AssertManager::GetRelativeFile(std::string("sdfdds_/") + fileNameSelf + ".dds");

        //if(fs::exists(ddsPath))
        //{
        //    continue;
        //}
        bool isfontFile = fxcc::FileUtil::IsFontFile(suffix);
        //if (!isfontFile)
        //{
        //    continue;
        //}
        std::string cachePath = fileNameSelf;
        FontFileToSDF::Desc desc;
        desc.fontPath = fontPath.string();
        desc.msdfSize = { 64, 64 };
        desc.msdfDir = "msdf";
        desc.ddsPath = fileNameNS + ".dds";

        FontFileToSDF file(desc);
        //file.createSDF();
        file.subSDF();
        //file.genDDSFile();

    }

    return 0;
}